package com.lyf.scm.common.util.date;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;

/**
 * @author libingbing
 * @date 2020/07/16
 */
public class MyDateUtil {

    /**
     * 根据开始时间和结束时间返回时间段内的时间集合
     *
     * @param beginDate 开始日期
     * @param endDate   结束日期
     * @return List<Date> 日期集合
     */
    public static List<Date> getDayDates(Date beginDate, Date endDate) {
        List<Date> lDate = new ArrayList<>();
        Date startTime = DateUtil.beginOfDay(beginDate);
        // 把开始时间加入集合
        lDate.add(startTime);
        Calendar cal = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        cal.setTime(startTime);
        boolean bContinue = true;
        while (bContinue) {
            // 根据日历的规则，为给定的日历字段添加或减去指定的时间量
            cal.add(Calendar.DAY_OF_MONTH, 1);
            // 测试此日期是否在指定日期之后
            if (endDate.after(cal.getTime())) {
                lDate.add(DateUtil.date(cal.getTime()));
            } else {
                break;
            }
        }
        // 把结束时间加入集合
        lDate.add(endDate);
        return lDate;
    }

    /**
     * 根据开始时间和结束时间返回时间段内的时间集合
     *
     * @param beginDate 开始日期
     * @param daysSize  间隔天数
     * @return List<Date> 日期集合
     */
    public static List<Date> getDayDates(Date beginDate, Integer daysSize) {
        List<Date> lDate = new ArrayList<>();
        // 把开始时间加入集合
        Date startTime = DateUtil.beginOfDay(beginDate);
        lDate.add(startTime);
        //根据间隔天数计算偏移日期
        Date endDate = DateUtil.offsetDay(startTime, daysSize);
        Calendar cal = Calendar.getInstance();
        // 使用给定的 Date 设置此 Calendar 的时间
        cal.setTime(startTime);
        boolean bContinue = true;
        while (bContinue) {
            // 根据日历的规则，为给定的日历字段添加或减去指定的时间量
            cal.add(Calendar.DAY_OF_MONTH, 1);
            // 测试此日期是否在指定日期之后
            if (endDate.after(cal.getTime())) {
                lDate.add(DateUtil.date(cal.getTime()));
            } else {
                break;
            }
        }
        // 把结束时间加入集合
        lDate.add(endDate);
        return lDate;
    }

    /**
     * 根据开始时间和结束时间获取每周开始日期和结束日期集合
     *
     * @param beginDate 开始日期
     * @param endDate   结束日期
     * @return List<Map < String, Date>> 计算后的每周范围集合 每个Map<String, Date>中有startTime和EndTime两个属性
     */
    public static List<Map<String, Date>> getWeekDates(Date beginDate, Date endDate) {
        List<Map<String, Date>> weekDates = new ArrayList<>();
        Map<String, Date> param;
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(beginDate);
        //获取结束时间的周一
        Date endMonday = DateUtil.beginOfWeek(endDate);
        //获取间隔周数
        long betweenSize = DateUtil.between(startMonday, endMonday, DateUnit.WEEK);

        for (int i = 0; i < betweenSize + 1; i++) {
            // 获取当前周的结束
            Date endDays = DateUtil.endOfWeek(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", startMonday);
            param.put("endTime", DateUtil.beginOfDay(endDays));
            weekDates.add(param);
            // 获取下周周一日期
            startMonday = DateUtil.offset(startMonday, DateField.WEEK_OF_MONTH, 1);
        }

        return weekDates;
    }

    /**
     * 根据传入日期获取每周开始及结束日期，
     *
     * @param date     指定日期
     * @param weekSize 指定输出周期的个数
     * @return List<Map < String, Date>> 计算后的每周范围集合 每个Map<String, Date>中有startTime和EndTime两个属性
     */
    public static List<Map<String, Date>> getWeekDates(Date date, Integer weekSize) {
        List<Map<String, Date>> weekDates = new ArrayList<>();
        Map<String, Date> param;
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(date);
        for (int i = 0; i < weekSize; i++) {
            // 获取当周开始日期和结束日期
            Date endDays = DateUtil.endOfWeek(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", startMonday);
            param.put("endTime", DateUtil.beginOfDay(endDays));
            weekDates.add(param);
            // 获取下周周一日期
            startMonday = DateUtil.offsetWeek(startMonday, 1);
        }

        return weekDates;
    }

    /**
     * 获取时间集合
     *
     * @param date
     * @param weekSize
     * @return
     */
    public static List<String> getDateLists(Date date, Integer weekSize) {
        List<Date> weekDates = new ArrayList<>();
        List<String> weekDate = new ArrayList<>();
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(date);
        for (int i = 0; i < weekSize; i++) {
            weekDates.add(startMonday);
            // 获取下周周一日期
            startMonday = DateUtil.offsetWeek(startMonday, 1);
        }
        weekDates.forEach(item -> {
            weekDate.add(DateUtil.formatDate(item));
        });
        return weekDate;
    }

    public static List<Map<String, String>> getWeekDateList(Date date, Integer weekSize) {
        List<Map<String, String>> weekDates = new ArrayList<>();
        Map<String, String> param;
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(date);
        for (int i = 0; i < weekSize; i++) {
            // 获取当周开始日期和结束日期
            Date endDays = DateUtil.endOfWeek(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", DateUtil.formatDate(startMonday));
            param.put("endTime", DateUtil.formatDate(endDays));
            weekDates.add(param);
            // 获取下周周一日期
            startMonday = DateUtil.offsetWeek(startMonday, 1);
        }

        return weekDates;
    }
    public static List<Map<String, String>> getWeekDateList(Date startDate, Date endDate) {
        List<Map<String, String>> weekDates = new ArrayList<>();
        Map<String, String> param;
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(startDate);
        //获取结束时间的周一
        Date endMonday = DateUtil.beginOfWeek(endDate);
        //获取间隔周数
        long betweenSize = DateUtil.between(startMonday, endMonday, DateUnit.WEEK);

        for (int i = 0; i < betweenSize + 1; i++) {
            // 获取当前周的结束
            Date endDays = DateUtil.endOfWeek(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", DateUtil.formatDate(startMonday));
            param.put("endTime", DateUtil.formatDate(endDays));
            weekDates.add(param);
            // 获取下周周一日期
            startMonday = DateUtil.offset(startMonday, DateField.WEEK_OF_MONTH, 1);
        }
        return weekDates;
    }

    /**
     * 获取前一周到后五周的日期数据
     *
     * @param date
     * @param weekSize
     * @return
     */
    public static List<Map<String, Date>> getWeekDatess(Date date, Integer weekSize) {
        List<Map<String, Date>> weekDates = new ArrayList<>();
        Map<String, Date> param;
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(date);
        for (int i = 0; i < weekSize; i++) {
            // 获取上周周一日期
            if (i == 0) {
                startMonday = DateUtil.offset(startMonday, DateField.WEEK_OF_MONTH, -1);
            } else {
                startMonday = DateUtil.offset(startMonday, DateField.WEEK_OF_MONTH, 1);
            }
            // 获取当周开始日期和结束日期
            Date endDays = DateUtil.endOfWeek(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", startMonday);
            param.put("endTime", DateUtil.beginOfDay(endDays));
            weekDates.add(param);
        }
        return weekDates;
    }

    /**
     * @param date
     * @param weekSize
     * @return
     */
    public static List<Map<String, String>> getWeekDateBySize(Date date, Integer weekSize) {
        List<Map<String, String>> weekDates = new ArrayList<>();
        Map<String, String> param;
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(date);
        for (int i = 0; i < weekSize; i++) {
            // 获取当周开始日期和结束日期
            Date endDays = DateUtil.endOfWeek(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", DateUtil.format(startMonday, "MM/dd"));
            param.put("endTime", DateUtil.format(DateUtil.beginOfDay(endDays), "MM/dd"));
            weekDates.add(param);
            // 获取下周周一日期
            startMonday = DateUtil.offset(startMonday, DateField.WEEK_OF_MONTH, 1);
        }

        return weekDates;
    }

    /**
     * 根据开始时间和结束时间获取每月开始日期和结束日期集合
     *
     * @param beginDate 开始日期
     * @param monthSize 指定输出周期的个数
     * @return List<Map < String, Date>> 计算后的每周范围集合 每个Map<String, Date>中有startTime和EndTime两个属性
     */
    public static List<Map<String, Date>> getMonthDates(Date beginDate, Integer monthSize) {
        List<Map<String, Date>> monthDates = new ArrayList<>();
        Map<String, Date> param;
        //获取开始时间所在月的1号
        Date startMonday = DateUtil.beginOfMonth(beginDate);
        for (int i = 0; i < monthSize; i++) {
            // 获取当月开始日期和结束日期
            Date endDays = DateUtil.endOfMonth(startMonday);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", startMonday);
            param.put("endTime", DateUtil.beginOfDay(endDays));
            monthDates.add(param);
            // 获取下周周一日期
            startMonday = DateUtil.offset(startMonday, DateField.MONTH, 1);
        }

        return monthDates;
    }

    /**
     * 根据开始时间和结束时间获取每月开始日期和结束日期集合
     *
     * @param beginDate 开始日期
     * @param endDate   结束日期
     * @return List<Map < String, Date>> 计算后的每周范围集合 每个Map<String, Date>中有startTime和EndTime两个属性
     */
    public static List<Map<String, Date>> getMonthDates(Date beginDate, Date endDate) {
        List<Map<String, Date>> weekDates = new ArrayList<>();
        Map<String, Date> param;
        //获取开始日期的当前月的开始
        Date startTime = DateUtil.beginOfMonth(beginDate);
        //获取间隔月个数
        long betweenSize = DateUtil.betweenMonth(beginDate, endDate, true);

        if (betweenSize < 0) {
            return null;
        }

        for (int i = 0; i < betweenSize + 1; i++) {
            // 获取当前月的结束
            Date endDays = DateUtil.endOfMonth(startTime);
            // 存入集合
            param = new LinkedHashMap<>();
            param.put("startTime", startTime);
            param.put("endTime", DateUtil.beginOfDay(endDays));
            weekDates.add(param);
            // 获取下月第一天日期
            startTime = DateUtil.offset(startTime, DateField.MONTH, 1);
        }

        return weekDates;
    }


    /**
     * 批量格式化日期格式（天）
     *
     * @param param   日期（天）集合
     * @param pattern 日期格式
     * @return List<String> 格式化后的日期集合
     */
    public static List<String> processDaysDate(List<Date> param, String pattern) {
        List<String> result = new ArrayList<>();
        for (Date date : param) {
            String rDate = DateUtil.format(date, pattern);
            result.add(rDate);
        }
        return result;
    }


    /**
     * 批量格式化日期格式（范围日期）
     *
     * @param param   日期（周/月）集合
     * @param pattern 日期格式
     * @return List<String> 格式化后的日期集合
     */
    public static List<String> processCyclesDate(List<Map<String, Date>> param, String pattern) {
        List<String> result = new ArrayList<>();
        for (Map<String, Date> map : param) {
            Date startTime = map.get("startTime");
            Date endTime = map.get("endTime");
            Date startDate = null;
            Date endDate = null;
            if (DateUtil.betweenWeek(startTime, endTime, true) > 0) {
                startDate = DateUtil.beginOfMonth(startTime);
                endDate = DateUtil.endOfMonth(endTime);
            } else {
                startDate = DateUtil.beginOfWeek(startTime);
                endDate = DateUtil.endOfWeek(endTime);
            }
            result.add(DateUtil.format(startDate, pattern) + "-" + DateUtil.format(endDate, pattern));
        }
        return result;
    }

    public static List<String> processCyclesDate2(List<Map<String, String>> param, String pattern) {
        List<String> result = new ArrayList<>();
        for (Map<String, String> map : param) {
            Date startTime = DateUtil.parseDate(map.get("startTime"));
            Date endTime = DateUtil.parseDate(map.get("endTime"));
            Date startDate = null;
            Date endDate = null;
            if (DateUtil.betweenWeek(startTime, endTime, true) > 0) {
                startDate = DateUtil.beginOfMonth(startTime);
                endDate = DateUtil.endOfMonth(endTime);
            } else {
                startDate = DateUtil.beginOfWeek(startTime);
                endDate = DateUtil.endOfWeek(endTime);
            }
            result.add(DateUtil.format(startDate, pattern) + "-" + DateUtil.format(endDate, pattern));
        }
        return result;
    }

    /**
     * 批量格式化日期格式（范围日期）
     *
     * @param param   日期（周/月）集合
     * @param pattern 日期格式
     * @return List<String> 格式化后的日期集合
     */
    public static List<String> processDate(List<Map<String, Date>> param, String pattern) {
        List<String> result = new ArrayList<>();
        for (Map<String, Date> map : param) {
            Date startTime = map.get("startTime");
            Date endTime = map.get("endTime");
            Date startDate = null;
            if (DateUtil.betweenWeek(startTime, endTime, true) > 0) {
                startDate = DateUtil.beginOfMonth(startTime);
            } else {
                startDate = DateUtil.beginOfWeek(startTime);
            }
            result.add(DateUtil.format(startDate, pattern));
        }
        return result;
    }

    /**
     * 获取结束时间
     *
     * @param dayList    天日期集合
     * @param cyclesList 范围日期集合
     * @return 当前集合最后一个元素
     */
    public static Date getLastDate(List<Date> dayList, List<Map<String, Date>> cyclesList) {
        if (dayList != null) {
            return dayList.get(dayList.size() - 1);
        } else if (cyclesList != null) {
            return cyclesList.get(cyclesList.size() - 1).get("endTime");
        } else {
            return null;
        }
    }

    /**
     * 根据开始时间和结束时间计算间隔周期
     *
     * @param beginDate 开始时间
     * @param endDate   结束时间
     * @return -1:小于一个月； 0: 小于一个季度； 1:大于一个季度
     */
    public static Integer getTypeBetweenDays(Date beginDate, Date endDate) {
        long betweenSize = DateUtil.between(beginDate, endDate, DateUnit.DAY);
        long monthDays = DateUtil.between(DateUtil.beginOfMonth(beginDate), DateUtil.endOfMonth(beginDate), DateUnit.DAY);
        long quarterlyDays = DateUtil.between(beginDate, DateUtil.offsetMonth(beginDate, 3), DateUnit.DAY);
        if (betweenSize < monthDays) {
            return -1;
        } else if (betweenSize > monthDays && betweenSize < quarterlyDays) {
            return 0;
        } else {
            return 1;
        }

    }

    /**
     * 获取前端展示date
     *
     * @param weekDateList
     * @return
     */
    public static List<String> getDateList(List<Map<String, Date>> weekDateList) {
        List<String> list = new ArrayList<>();
        weekDateList.forEach(item -> {
            list.add(DateUtil.format(item.get("startTime"), "MM.dd"));
        });
        return list;
    }


    /**
     * 处理开始时间，默认查询的时间是从上一周开始往后14周的数据,
     * 如果前端传入的开始时间在当前时间上个周一之前，查询开始时间不用调整，
     * 如果前端传入的开始时间在当前时间上个周一之后，查询开始时间调整为上个周一开始
     */
    public static Date dealBeginDate(Date date) {
        if (DateUtil.beginOfWeek(date).getTime() + DateUnit.WEEK.getMillis() >= DateUtil.beginOfWeek(new Date()).getTime()) {
            return DateUtil.offsetWeek(DateUtil.beginOfWeek(new Date()), -1);
        }
        return DateUtil.beginOfWeek(date);
    }


    /**
     * 返回前1周的周一
     *
     * @param date
     * @return
     */
    public static Date getLastDate(Date date) {
        //获取开始时间的周一
        Date startMonday = DateUtil.beginOfWeek(date);
        for (int i = 0; i < 1; i++) {
            startMonday = DateUtil.offset(startMonday, DateField.WEEK_OF_MONTH, -1);
        }
        return startMonday;
    }

    /**
     * 处理当前周是否跨月，跨了几天
     */
    public static Map<Date, BigDecimal> dealCrossMonth(Date endTime) {
        Map<Date, BigDecimal> decimalMap = new LinkedHashMap<>();
        //判断当周的结束时间是在当月的第几天
        int endDay = DateUtil.dayOfMonth(endTime);
        if (endDay < 7) {
            decimalMap.put(endTime, BigDecimal.valueOf(endDay).divide(new BigDecimal(7.00), 3, BigDecimal.ROUND_HALF_UP));
        } else {
            decimalMap.put(endTime, BigDecimal.ONE);
        }
        return decimalMap;
    }

    public static String getDateAfterWeek(String currentDate, int afterWeeks) {
        return LocalDate.parse(currentDate).plusWeeks(afterWeeks).toString();
    }
}
